﻿using BLToolkit.Data.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace SettlersAuto.Data.Entities
{
    public static class TradeSlotExtension
    {
        [MethodExpression("PriceExpression")]
        public static decimal Price(this TradeSlot ts)
        {
            return (decimal)ts.DemandQuantity / ts.OfferQuantity; 
        }

        public static Expression<Func<TradeSlot, decimal>> PriceExpression()
        {
            return (ts) => (decimal)ts.DemandQuantity / ts.OfferQuantity;
        }

        public static IQueryable<TradeSlot> WithProducts(this IQueryable<TradeSlot> query, string offerProduct, string demandProduct)
        {
            return query.Where(ts => ts.OfferProductName == offerProduct && ts.DemandProductName == demandProduct);
        }

        public static IQueryable<TradeSlot> FromSession(this IQueryable<TradeSlot> query, int sessionId)
        {
            return query.Where(ts => ts.TradeSessionId == sessionId);
        }

        public static IQueryable<TradeSlot> WithTime(this IQueryable<TradeSlot> slots, DateTime toDate, TimeSpan? windowDuration = null)
        {
            var duration = windowDuration ?? TimeSpan.FromDays(5);
            var fromDate = toDate - duration;
            return slots.Where(ts => ts.TradeSession.Timestamp > fromDate && ts.TradeSession.Timestamp < toDate);
        }

        public static TradeSlot Invert(this TradeSlot slot)
        {
            return new TradeSlot
            {
                TradeSlotId = slot.TradeSlotId,
                TradeSessionId = slot.TradeSessionId,
                TradeSession = slot.TradeSession,
                SenderId = slot.SenderId,
                SenderName = slot.SenderName,
                LotsCount = slot.LotsCount,
                OfferProductName = slot.DemandProductName,
                OfferQuantity = slot.DemandQuantity,
                DemandProductName = slot.OfferProductName,
                DemandQuantity = slot.OfferQuantity
            };
        }

        public static decimal? GetAveragePrice(this IQueryable<TradeSlot> slots)
        {
            var sampleSize = 200;
            var topPercentile = 95;
            var bottomPercentile = 50;

            var sampleSlots = slots
                .OrderByDescending(ts => ts.TradeSession.Timestamp)
                .Take(sampleSize);

            var count = sampleSlots.Count();
            var take = count * (100 - bottomPercentile) / 100;
            var skip = count * (100 - topPercentile) / 100;
            sampleSlots = sampleSlots.OrderBy(ts => ts.Price()).Take(take).Skip(skip);

            var num = sampleSlots.Count();
            if (num < 5)
            {
                return null;
            }

            var sum = sampleSlots.Sum(ts => ts.Price());

            return (decimal)sum / num;
        }
    }
}
